import json
import os
import re
import time
from threading import Thread
from botpy.user import Member
from botpy.message import Message

from src.base.settings import Settings
from src.base.cmd_executor import CmdExecutor
from src.conf.robot_conf import RobotConf
from src.conf.conf_manager import ConfManager
from src.logger import Logger
from src.process import ExeControl
from src.utils.tool import Tool
from src.utils.method import Method
from src.bot.handler import Handler


class Robot:
    def __init__(self, conf: ConfManager, logger: Logger, dls: ExeControl):
        self.__conf = conf
        self.__dls = dls
        self.__logger = logger
        self.__handler = Handler(conf, logger)
        self.__ini: RobotConf = self.__conf.robot_conf
        self.__data_dir = os.path.join(Settings.WORK_PATH, Settings.CONF_DIR, "数据")
        self.executor = CmdExecutor(self.__do_cmd_list)
        self.group_cmd = self.__conf.robot_cmd.content
        # 内部参数
        self.online = True
        self.__thread_list = []
        self.__data = {}
        self.load_data()
        self.init_data()

    # 发送消息至群聊
    def send_group_message(self, message: str) -> None:
        self.__logger.add_robot_msg(message)

    # 处理所有内置命令
    def deal_cmd_log(self) -> None:
        # （Minimouse添加了在发送消息的同时执行指令的功能）
        for cmd_list in self.__logger.get_cmd_lists():
            self.executor.submit(cmd_list)

    # 处理QQ消息事件
    def deal_event_log(self) -> None:
        # 获取队列大小并执行
        size = self.__handler.event.qsize()
        for _ in range(size):
            new_event: dict = self.__handler.event.get()
            try:
                # 判断是否为群消息
                if new_event["type"] == "message":
                    new_msg: Message = new_event["data"]
                    text: str = new_msg.content
                    # 获取发送者信息
                    nickname: str = new_msg.author.username
                    card_name: str = nickname
                    qq: str = new_msg.author.id
                    # 分权限执行命令
                    for robot_cmd in self.group_cmd:
                        cmd_header: str = robot_cmd.get("识别头")
                        cmd_reg: str = robot_cmd.get("正则")
                        strict_match = robot_cmd.get("严格匹配", False)
                        cmd_reg_res = []
                        # 存在识别头时
                        if cmd_header is not None:
                            # 严格匹配处理
                            if strict_match:
                                if text != cmd_header:
                                    continue
                                plain: str = text
                            # 头部匹配处理
                            elif re.match(cmd_header, text):
                                plain: str = text.replace(cmd_header, "", 1).strip()
                            else:
                                continue
                        elif cmd_reg and re.findall(cmd_reg, text):
                            # 不存在识别头但正则匹配成功
                            plain: str = text
                        else:
                            continue
                        # 有正则时获取文本匹配结果
                        if cmd_reg:
                            cmd_reg_res = re.findall(cmd_reg, plain)
                        # 执行源命令转为列表
                        execute_cmd = robot_cmd.get("执行命令", [])
                        if type(execute_cmd) is str:
                            execute_cmd = [execute_cmd]
                        # 设置关联记录时，启用%r占位符
                        relate_record: str = robot_cmd.get("关联记录", "")
                        rec_list = []
                        if relate_record:
                            relate_record = relate_record.replace("%p", plain) \
                                .replace("%n", card_name).replace("%i", nickname) \
                                .replace("%q", qq)
                            relate_record = Method.deal_placeholder(relate_record, cmd_reg_res)
                            args = relate_record.split(" ")
                            if len(args) != 2:
                                self.send_group_message("请指定用户ID")
                            else:
                                record, rec_id = args
                                rec_list = self.search_data(record, rec_id)
                        # 替换占位符生成命令列表
                        cmd_list = []
                        for cmd in execute_cmd:
                            cmd: str = cmd.replace("%p", plain) \
                                .replace("%n", card_name).replace("%i", nickname) \
                                .replace("%q", qq)
                            cmd = Method.deal_placeholder(cmd, cmd_reg_res)
                            # 处理%r占位符
                            if rec_list and re.search("%r", cmd):
                                for rec in rec_list:
                                    cmd_list.append(cmd.replace("%r", rec))
                            else:
                                cmd_list.append(cmd)
                        # 分权限处理
                        if robot_cmd.get("全员可用") or qq in self.__handler.owners \
                                or qq in robot_cmd.get("指定成员可用", []) \
                                or (self.__ini.allow_group_admin and qq in self.__handler.admin_id_list):
                            # 是否显示执行命令
                            if self.__conf.conf.show_dls_execute:
                                self.__logger.add_log(f"[DLS][RES] 执行命令: {', '.join(cmd_list)}")
                            if robot_cmd.get("记录"):
                                self.count_and_do_cmd(robot_cmd, plain, qq, cmd_list)
                            else:
                                self.executor.submit(cmd_list)
                elif new_event["type"] == "member_remove":
                    member: Member = new_event["member"]
                    self.clear_data(member.user.id)
            except Exception as err:
                self.__logger.add_log(f"\n[DLS][ERROR] 异常的消息！ 错误: {err}  消息json: {new_event}\n")

    # 事件处理
    def deal_all(self) -> None:
        # 开始逻辑循环
        while self.online:
            time.sleep(0.4)
            self.deal_cmd_log()
            # ws连接断开时挂起
            if not self.__handler.alive:
                continue
            self.deal_event_log()

    def load_data(self) -> None:
        """从文件加载数据"""
        # 数据文件夹不存在时创建
        if not os.path.exists(self.__data_dir):
            os.mkdir(self.__data_dir)
        file_path = os.path.join(self.__data_dir, "命令次数记录.json")
        # 文件不存在时不加载
        if not os.path.exists(file_path):
            return
        try:
            with open(file_path, "rb") as f:
                self.__data = json.loads(f.read())
        except json.decoder.JSONDecodeError:
            print("[DLS][ERROR] 数据文件格式有误！无法加载")

    def init_data(self) -> None:
        """初始化数据对象"""
        if not self.__data.get("cmdTimes"):
            self.__data["cmdTimes"] = {}
        for robot_cmd in self.group_cmd:
            if not robot_cmd.get("记录"):
                continue
            record = robot_cmd["记录"]
            if not self.__data["cmdTimes"].get(record):
                self.__data["cmdTimes"][record] = {}

    def search_data(self, record: str, user_id: str) -> list:
        """查询记录中指定用户的内容"""
        if self.__data["cmdTimes"][record].get(user_id):
            ret: list = self.__data["cmdTimes"][record][user_id]["记录值"]
            return ret
        return []

    def reset_data(self, record: str, qq: str) -> bool:
        """重置记录中指定用户的内容"""
        if self.search_data(record, qq):
            self.__data["cmdTimes"][record][qq] = {"次数": 0, "记录值": []}
            self.save_data()
            return True
        return False

    def clear_data(self, user_id: str):
        """重置记录并执行回调命令"""
        for robot_cmd in self.group_cmd:
            if not robot_cmd.get("记录"):
                continue
            record = robot_cmd["记录"]
            player_names: list = self.search_data(record, user_id)
            # 退群触发相关命令
            self.__leave_callback_cmd(robot_cmd, player_names)
            if robot_cmd.get("退群重置") and player_names:
                self.send_group_message(f"{user_id}记录移除: {', '.join(player_names)}")
                self.reset_data(record, user_id)

    def save_data(self) -> None:
        """保存记录至文件"""
        file_path = os.path.join(self.__data_dir, "命令次数记录.json")
        self.save_data_to_file("cmdTimes", self.__data["cmdTimes"], file_path)

    # 命令记录与执行
    def count_and_do_cmd(self, robot_cmd: dict, plain: str, user_qq: str, cmd_list: list) -> None:
        limit_times = robot_cmd.get("限制次数", 0)
        # 用户初始化
        if not self.__data["cmdTimes"][robot_cmd["记录"]].get(user_qq):
            self.__data["cmdTimes"][robot_cmd["记录"]][user_qq] = \
                {"次数": 0, "记录值": []}
        user_dict: dict = self.__data["cmdTimes"][robot_cmd["记录"]][user_qq]
        # 判断与计数
        if limit_times == 0:
            self.send_group_message("记录还未启用！")
        elif user_dict["次数"] < limit_times:
            if self.__do_cmd_list(cmd_list):
                user_dict["次数"] += 1
                user_dict["记录值"].append(plain)
                self.send_group_message(f'ID: {user_qq}\n记录值: {", ".join(user_dict["记录值"])}')
                # 保存记录至文件
                self.__data["cmdTimes"][robot_cmd["记录"]][user_qq] = user_dict
                self.save_data()
            else:
                self.send_group_message("命令执行失败, 记录被撤销！")
        elif limit_times == -1 or user_qq in self.__handler.owners:
            self.executor.submit(cmd_list)
            user_dict["次数"] += 1
            user_dict["记录值"].append(plain)
            self.send_group_message(f'ID: {user_qq}\n记录值: {", ".join(user_dict["记录值"])}')
            # 保存记录至文件
            self.__data["cmdTimes"][robot_cmd["记录"]][user_qq] = user_dict
            self.save_data()
        else:
            self.send_group_message(f'命令次数已用完！\nID: {user_qq}\n记录值: {", ".join(user_dict["记录值"])}')

    # 移除指定记录
    def times_reset(self, record: str, qq: str) -> str:
        if self.reset_data(record, qq):
            return "移除记录成功"

    # 查询指定记录
    def times_search(self, record: str, qq: str) -> str:
        data = self.search_data(record, qq)
        if data:
            return f'ID: {qq}\n记录值: {", ".join(data)}'
        return f"未找到相关记录"

    # 清理退群人员的记录并执行回调命令
    def clear_left_member(self):
        mem_id_list = self.__handler.member_id_list
        # 异常状况列表为空，不进行操作
        if not mem_id_list:
            self.send_group_message("无法获取成员列表，清理失败")
            return
        for robot_cmd in self.group_cmd:
            if not robot_cmd.get("记录"):
                continue
            record = robot_cmd["记录"]
            all_data = self.__data["cmdTimes"][record]
            for user_id in all_data:
                if user_id in mem_id_list:
                    continue
                rec_list: list = self.search_data(record, user_id)
                # 退群触发
                self.__leave_callback_cmd(robot_cmd, rec_list)
                if robot_cmd.get("退群重置") and rec_list:
                    self.reset_data(record, user_id)
                    self.send_group_message(f"ID: {user_id}\n记录移除: {', '.join(rec_list)}")
        self.send_group_message("清理已完成")

    def __leave_callback_cmd(self, robot_cmd, rec_list):
        # 退群触发相关命令
        if robot_cmd.get("退群触发"):
            callback_cmd = robot_cmd.get("退群触发", [])
            if type(callback_cmd) is str:
                callback_cmd = [callback_cmd]
            cmd_list = []
            for cmd in callback_cmd:
                if re.search("%r", cmd):
                    for rec in rec_list:
                        cmd_list.append(cmd.replace("%r", rec))
                else:
                    cmd_list.append(cmd)
            self.executor.submit(cmd_list)

    # 文件的保存
    def save_data_to_file(self, data_key, data_content, file_path: str) -> None:
        data_dict = {data_key: data_content}
        try:
            with open(file_path, "w", encoding="utf-8") as f:
                f.write(json.dumps(data_dict, sort_keys=True, indent=2, ensure_ascii=False))
        except Exception as err:
            self.__logger.add_log(f"[DLS][ERROR] 文件{file_path}保存失败！错误原因: {err}！")

    # 执行命令
    def __do_cmd_list(self, cmd_list: list) -> bool:
        # 判断命令链是否执行成功
        all_cmd_success = True
        for cmd in cmd_list:
            # 被禁用的命令将被屏蔽
            for limit_cmd in self.__conf.conf.limit_dls_cmd:
                if re.match(limit_cmd, cmd):
                    cmd = ""
            # 开始执行命令
            if cmd == "start":
                self.__dls.exe_start()
            elif cmd == "stop":
                self.__dls.exe_stop()
            elif cmd == "restart":
                self.__dls.exe_restart()
            elif cmd == "status":
                self.send_group_message(Tool.get_status())
            elif re.match("sys", cmd):
                sys_cmd = cmd.replace("sys", "", 1).strip()
                os.system(sys_cmd)
            elif re.match("wait", cmd):
                wait_time = cmd.replace("wait", "", 1).strip()
                try:
                    time.sleep(int(wait_time))
                except ValueError:
                    pass
            elif re.match("group", cmd):
                group_text = cmd.replace("group", "", 1).strip()
                self.__logger.add_robot_msg(group_text)
            elif re.match("cmd", cmd):
                cmd = cmd.replace("cmd", "", 1).strip()
                if not self.__dls.execute_cmd(cmd):
                    all_cmd_success = False
            elif re.match("chatJava", cmd):
                msg_text = cmd.replace("chatJava", "", 1).strip()
                raw_json = {"text": msg_text}
                cmd = f"tellraw @a {json.dumps(raw_json)}".replace("\'", "\"")
                if not self.__dls.execute_cmd(cmd, mute=True):
                    all_cmd_success = False
            elif re.match("chat", cmd):
                msg_text = cmd.replace("chat", "", 1).strip()
                raw_json = {"rawtext": [{"text": msg_text}]}
                cmd = f"tellraw @a {json.dumps(raw_json)}".replace("\'", "\"")
                if not self.__dls.execute_cmd(cmd, mute=True):
                    all_cmd_success = False
            elif re.match("motdpe", cmd):
                cmd = cmd.replace("motdpe", "", 1).strip()
                self.send_group_message(Tool.motd_pe(cmd))
            elif re.match("blackbe", cmd):
                cmd = cmd.replace("blackbe", "", 1).strip()
                self.send_group_message(Tool.black_be(cmd))
            elif re.match("xuid", cmd):
                cmd = cmd.replace("xuid", "", 1).strip()
                self.send_group_message(Tool.xuid_search(cmd))
            elif re.match("timesReset", cmd):
                cmd = cmd.replace("timesReset", "", 1).strip()
                args = cmd.split(" ")
                if len(args) != 2:
                    self.send_group_message("请指定用户ID")
                    all_cmd_success = False
                record, qq = args
                self.send_group_message(self.times_reset(record, qq))
            elif re.match("timesSearch", cmd):
                cmd = cmd.replace("timesSearch", "", 1).strip()
                args = cmd.split(" ")
                if len(args) != 2:
                    self.send_group_message("请指定用户ID")
                    all_cmd_success = False
                record, qq = args
                self.send_group_message(self.times_search(record, qq))
            elif re.match("clearLeftMem", cmd):
                self.clear_left_member()
            elif re.match("startBak", cmd):
                self.__dls.bak_world()
            elif re.match("dlsload", cmd):
                self.__conf.reload()
                self.reload()
        return all_cmd_success

    # 启动线程
    def start(self) -> None:
        th = Thread(target=self.deal_all, daemon=True)
        th.start()
        self.__thread_list.append(th)
        # 未启用时不启动bot连接
        if not self.__ini.on:
            return
        self.__handler.auto_run()
        self.__logger.add_log(f"[DLS] 机器人模块已启用")

    # 关闭线程
    def stop(self) -> None:
        # 关闭线程
        self.online = False
        for th in self.__thread_list:
            try:
                th.join(5)
            except Exception as e:
                print(f"\033[31m[DLS][ERROR] {th}异常的线程关闭！错误：{e}\033[0m")
        self.__thread_list.clear()

    # 重加载配置
    def reload(self) -> None:
        self.__handler.reload()
        self.group_cmd = self.__conf.robot_cmd.content
        self.load_data()
        self.init_data()
